import cv2
import numpy as np
import math
import random


def callback():  # 注意这里createTrackbar会向其传入参数即滑动条地址（几乎用不到），所以必须写一个参数
    pass


def QMainWindow():

    cap = cv2.VideoCapture(0)

    cv2.namedWindow('frame')
    cv2.resizeWindow('frame', 600, 800)
    cv2.createTrackbar("change", "frame", 100, 255, callback)  #活动条

    while (cap.isOpened()):
        ret, image = cap.read()  # 读取摄像头每帧图片

        image = cv2.flip(image, 1)  #反转

        cv2.rectangle(image, (100, 100), (300, 300), (0, 0, 255), 0)  # 用红线画出手势识别框

        # 滑动条控制颜色  可通过滑动条来调整亮度,提高识别率
        value = cv2.getTrackbarPos('change', 'frame')
        image_dst = np.uint8(image / 100 * value)

        roi = image_dst[100:300, 100:300]  # 选取图片中固定位置作为手势输入
        kernel = np.ones((2, 2), np.uint8)

        # 进行高斯滤波
        lower_skin = np.array([0, 28, 70], dtype=np.uint8)
        upper_skin = np.array([20, 255, 255], dtype=np.uint8)

        mask = cv2.inRange(roi, lower_skin, upper_skin)  #设阈值，去除背景部分第一个参数：hsv指的是原图
        # lower_red指的是图像中低于这个lower_red的值，图像值变为0
        #upper_red指的是图像中高于这个upper_red的值，图像值变为0
        # 在lower_red～upper_red之间的值变成255
        mask = cv2.dilate(mask, kernel, iterations=4)
        mask = cv2.GaussianBlur(mask, (3, 3), 100)

        # 基于hsv的肤色检测
        hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)

        # 进行高斯滤波
        mask = cv2.inRange(hsv, lower_skin, upper_skin)
        mask = cv2.dilate(mask, kernel, iterations=4) #膨胀运算
        mask = cv2.GaussianBlur(mask, (5, 5), 100)

        # 找出轮廓image：参数是寻找轮廓的图像；
        # mode：参数表示轮廓的检索模式，有四种（本文介绍的都是新的cv2接口）：
        #   cv2.RETR_TREE：建立一个等级树结构的轮廓。
        # method：轮廓的近似办法：
        #    cv2.CHAIN_APPROX_SIMPLE：压缩水平方向，垂直方向，对角线方向的元素，只保留该方向的终点坐标，例如一个矩形轮廓只需4个点来保存轮廓信息

        contours, h = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
        # 返回一个list，list中每个元素都是图像中的一个轮廓
        cnt = max(contours, default=0, key=lambda x: cv2.contourArea(x)) #面积
        # epsilon = 0.0005 * cv2.arcLength(cnt, True)
        approx = cv2.approxPolyDP(cnt, 0.05, True) #多边形拟合曲线
        hull = cv2.convexHull(cnt) #计算几何之凸包  cnt坐标点
        areahull = cv2.contourArea(hull)
        areacnt = cv2.contourArea(cnt)
        arearatio = ((areahull - areacnt) / areacnt) * 100

        # 求出凹凸点
        hull = cv2.convexHull(approx, returnPoints=False)
        defects = cv2.convexityDefects(approx, hull)

        # 定义凹凸点个数初始值为0
        l = 0
        try:
            for i in range(defects.shape[0]):
                s, e, f, d, = defects[i, 0]
                start = tuple(approx[s][0])
                end = tuple(approx[e][0])
                far = tuple(approx[f][0])
                pt = (100, 100)

                a = math.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2)#根号
                b = math.sqrt((far[0] - start[0]) ** 2 + (far[1] - start[1]) ** 2)
                c = math.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2)
                s = (a + b + c) / 2
                ar = math.sqrt(s * (s - a) * (s - b) * (s - c))

                # 手指间角度求取
                angle = math.acos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c)) * 57

                if angle <= 90 and d > 20:
                    l += 1
                    cv2.circle(roi, far, 3, [255, 0, 0], -1)
                cv2.line(roi, start, end, [0, 255, 0], 2)  # 画出包络线
            l += 1
            font = cv2.FONT_HERSHEY_SIMPLEX

            # 条件判断，知道手势后想实现的功能
            if l == 1:
                if areacnt < 2000:
                    cv2.putText(image_dst, "Please put hand in the window", (0, 50), font, 2, (0, 0, 255), 3,
                                cv2.LINE_AA)
                else:
                    if arearatio < 12:
                        cv2.putText(image_dst, '0', (0, 50), font, 2, (0, 0, 255), 3, cv2.LINE_AA)

                    elif arearatio < 17.5:
                        cv2.putText(image_dst, "1", (0, 50), font, 2, (0, 0, 255), 3, cv2.LINE_AA)
                    else:
                        cv2.putText(image_dst, '1', (0, 50), font, 2, (0, 0, 255), 3, cv2.LINE_AA)
            elif l == 2:
                cv2.putText(image_dst, '2', (0, 50), font, 2, (0, 0, 255), 3, cv2.LINE_AA)
            elif l == 3:
                if arearatio < 27:
                    cv2.putText(image_dst, '3', (0, 50), font, 2, (0, 0, 255), 3, cv2.LINE_AA)
                else:
                    cv2.putText(image_dst, '3', (0, 50), font, 2, (0, 0, 255), 3, cv2.LINE_AA)
            elif l == 4:
                cv2.putText(image_dst, '4', (0, 50), font, 2, (0, 0, 255), 3, cv2.LINE_AA)
            elif l == 5:
                cv2.putText(image_dst, '5', (0, 50), font, 2, (0, 0, 255), 3, cv2.LINE_AA)
            # cv2.imshow('frame',frame)
            cv2.imshow('mask', mask)
            cv2.imshow('frame', image_dst)

            key = cv2.waitKey(25) & 0xFF
            if key == ord('q'):  # 键盘q键退出
                break
        except:
            pass

    cv2.destroyAllWindows()
    cap.release()


QMainWindow()



